﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;

namespace ThreadText
{
    class Program
    {
        static AutoResetEvent autoEvent;
        static void Main(string[] args)
        {
            //LockOne lockOne = new LockOne();
            //lockOne.FirstThread(); //在多线程中不能用顺序来考虑的 并不是当该方法结束后才执行下一个方法
            //Console.ReadLine();
            //LockTwo lockTwo = new LockTwo();
            //lockTwo.SecondThread();
            //ThreadUseAndConstruction();

            //永远走不下去，主线程已经堵塞住了。
            //DeadLock();
            
            autoEvent = new AutoResetEvent(false);
            Box box = new Box();
            lock (box)
            {
                //锁保证其他线程不会中断当前线程的操作，lock 关键字可确保当一个线程位于代码的临界区时，另一个线程不会进入该临界区。
                box.Add("key", 12);
                ThreadPool.QueueUserWorkItem(SetBox, box);
                //等待其他线程，致使lock(box)无法释放，造成死锁。
                autoEvent.WaitOne();
                //Thread.Sleep(10000);
                
            }
            Console.WriteLine("ok");
            Console.ReadLine();
        }
        /*0
         * First    //该线程sleep1000
         * 0
         * Second  //该线程sleep1000
         * 1        // 该线程sleep500
         * Change the value in locking
         * 1
         * ThreadOne
         * 0
         * SecondThreadOne
         * 1
         * Can't change the value in locking         
         */
        //ThreadTwo中的lock需要等到ThreadOne种lock释放后才开始运行,释放之前一直处于等待状态,这就是标志的表现
        /// <summary>
        /// 设置了线程池的最大量为12，则运行时只能有12个线程，其他的线程需要等运行的线程结束后才运行（callback执行结束了）
        /// 原来最小线程数量为5时，只有4个线程可以立即执行。经过进一步尝试，最小线程数量为10时，也只有9个线程可以立即执行
        /// 在超过了最小量后，增长线程每秒不超过2个
        /// </summary>
        //public static void ThreadUseAndConstruction()
        //{
        //    ThreadPool.SetMaxThreads(12, 12);//线程池最大数12
        //    ThreadPool.SetMinThreads(5, 5);//最小量5
        //    Stopwatch stopwatch = new Stopwatch();
        //    stopwatch.Start();
        //    WaitCallback callback = index =>
        //                                {
        //                                    Console.WriteLine(String.Format("{0}: Task {1} started", stopwatch.Elapsed,
        //                                                                    index));
        //                                    Thread.Sleep(10000);
        //                                    Console.WriteLine(String.Format("{0}: Task {1} finished", stopwatch.Elapsed,
        //                                                                    index));
        //                                };
        //    for (int i = 0; i < 20; i++)
        //    {
        //        ThreadPool.QueueUserWorkItem(callback, i);
        //    }
        //}
        /*00:00:00.0028309: Task 0 started
            00:00:00.0079552: Task 1 started
            00:00:00.0080033: Task 2 started
            00:00:00.0081628: Task 3 started
            00:00:01.0058442: Task 4 started
            00:00:01.5039911: Task 5 started
            00:00:02.0048392: Task 6 started
            00:00:02.5051786: Task 7 started
            00:00:03.0051154: Task 8 started
            00:00:04.0048998: Task 9 started
            00:00:05.0053109: Task 10 started
            00:00:06.0068503: Task 11 started
            00:00:10.0079897: Task 1 finished
            00:00:10.0084587: Task 12 started
            00:00:10.0087316: Task 2 finished
            00:00:10.0079939: Task 3 finished
            00:00:10.0090849: Task 14 started
            00:00:10.0088292: Task 0 finished
            00:00:10.0101870: Task 15 started
            00:00:10.0089327: Task 13 started
            00:00:11.0059534: Task 4 finished
            00:00:11.0063235: Task 16 started
            00:00:11.5040658: Task 5 finished
            00:00:11.5044820: Task 17 started
            00:00:12.0051271: Task 6 finished
            00:00:12.0064219: Task 18 started
            00:00:12.5061198: Task 7 finished
            00:00:12.5074655: Task 19 started
            00:00:13.0052512: Task 8 finished
            00:00:14.0052185: Task 9 finished
            00:00:15.0064023: Task 10 finished
            00:00:16.0074270: Task 11 finished
            00:00:20.0085632: Task 12 finished
            00:00:20.0094829: Task 14 finished
            00:00:20.0104810: Task 13 finished
            00:00:20.0119368: Task 15 finished
            00:00:21.0066450: Task 16 finished
            00:00:21.5045454: Task 17 finished
            00:00:22.0116638: Task 18 finished
            00:00:22.5107089: Task 19 finished
            1.0秒：任务0至任务3，共计4个任务开始执行。 
            2.1至3秒：任务4至任务8依次执行，间隔为0.5秒。 
            3.3至6秒：任务8至任务11依次执行，间隔为1秒。 
            4.10秒：任务0至任务3执行完成，任务12至任务15开始执行。 
            5.11至12.5秒：每执行完一个旧任务（4至7)，便立即开始一个新任务（16至19）。 
            6.13至22.5秒：剩余任务（8至19）依次结束。　
*/

        static void WaitCallBack(Object handle)
        {

            ManualResetEvent waitHandle = handle as ManualResetEvent;
            if (waitHandle==null)
            {
                return;
            }
            for(int i = 0;i<10;i++)
            {
                ThreadPool.QueueUserWorkItem(state =>
                                                 {
                                                     int index = (int)state;
                                                     if(index == 9)
                                                     {
                                                         waitHandle.Set();
                                                     }
                                                     else
                                                     {
                                                         waitHandle.WaitOne();
                                                     }

                                                 },i);
            }
        }
        /*在上面的代码中，waitHandle将永远阻塞。
        因为我们放入线程池的10个任务，只有最后一个会将waitHandle打开，其余任务也统统阻塞在这个waitHandle上。
        但是请注意，我们使用SetMaxThreads方法把最大线程数限制为5，这样第10个任务根本无法执行，从而进入了死锁。
        避免这个问题最简单的做法是增加最大线程数，但是这还是会产生许多无法工作的线程，造成资源的浪费。
        因此，最好的做法是重新设计并行算法，并且时刻记住：“不要阻塞线程池里的线程”。
         */
        public static void DeadLock()
        {
            ManualResetEvent waitHandle = new ManualResetEvent(false);
            ThreadPool.SetMaxThreads(5, 5);
            ThreadPool.QueueUserWorkItem(WaitCallBack, waitHandle);
            //堵塞主线程
            waitHandle.WaitOne();
        }

        static void SetBox(object obj)
        {
            Box box = obj as Box;
            //等待上一个锁释放
            //已成死锁
            box.Add("key", 12);
            //通知其他线程可继续执行
            autoEvent.Set();
            Console.WriteLine(12);
        }
    }

    public class Box
    {
        private static Dictionary<string, object> dic = new Dictionary<string, object>();
        public void Add(string key, object value)
        {
            //如果实例可以被公共访问，则有可能导致死锁。
            //正确做法应定义 private 对象来锁定, 或 private static 对象变量来保护所有实例所共有的数据。
           
            lock (this)
            {
                dic[key] = value;
            }
        }
    }
}
